package cn.runagain.demo.wechatminiappdemo.utils;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import javax.crypto.IllegalBlockSizeException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;

/**
 * created by
 *
 * @author tuxianchao
 * @date 2018/7/2 下午6:07
 */
public class WechatDataDecrypt {

    public static void main(String[] args) {
        String appId = "wx4f4bc4dec97d474b";
//        微信步数加密数据数据
//        String encryptedData = "W9Ruco9RvBSIsX3eNecbRJ18khdpieU98kTD6HvInpxl1/LuVY8kfeTyadJIaVzsw7ZyE1pAVKO4FCHQY2ugMXn9DgIG8qzDQA7xjQF+sci5PQzVSSfq+Mo5xvlDap2bFw4Jp6OStYUef1H9rXDWEvExzKPYxf+rNgKYxZ3v6Pa6BbQeFnQn2+tHTHIrKBTZZ5UPXQp5lhRwi5KVZ5NS8PXtvGCA32rHAs3JuG7FB5sh/hYyVOGdkXbBrmwgzufRFUtoyIGACWGccG2MCy1BuY2xtPsxWZtAhpK9vX5gaEMXKgbsYV2RJeuMVRxObcWL2EY+gddwT4+akyvItOyZNVSORamgHyX7PwOQJtCSFL8Y16wVcwJ5uPzytb6dOXJN1y6EAYM15CA2azrCljvvXtDMXljvTAautj1CJyDMdFiFLrumoCBg/SmteCZiF6ELcxLmxiqv4jP3I96Ya6acLpQtUW2HaC2yfhPnQFDu8dRlam5mtz2f5XPNp6atL/3ThZhSOCRO5lraOv5+C4Bl0VYvnP897fmPNpymzkHUno88OzmG6vnGPA4x1D5oi/UnO+tuvKenlaMzcwlob37uFkpcZSmk32rulFCt1Aa/Yfw8DspaUO5pa9uP3szYmDB6kKKrIYVFj7V/rOleH7AHIlsyOyQlZ1GXvdimsPgn0XU7/KoreGrDgpedg5RQA/QG6t0P/X5uUPFZh0EUgzX1HEETX1X9KfARS8x/GmugVGZLP0gPc/8jnDSQ+FUotEsKNV4KjiCKawYvykdMEUqtRjSymtlNK6xQsX/JAj2Qfm+xqgsy6913EPREj+MFBrGepSpOCvXq/SG2ioBZIbQcMblq5MBAk0DJCR7ASApZAygqlB75WXm4eKBW1gVWQ8Ng1O3TAMvU7BjJNZrVFGFjNKY0dci1pcXk9S8z41sRBm9KPMJtdt0T1ulfn1EbQs/y00thmQ9IcDP33CO4FD13F7uXBDtryk31fqH7UKJEkzbP+Q1UQQmcrkRRUJRnpPKItQDHlQW1orYMZAqZHgLDw9zuKSQwu30ycxZvJIrV+kWXFvaXgQrel39z5IulR1Q4EC3okTsjDDfT+vmN/6Bkls2nBU+zt37zl056G2n8Q7R7Hvivf6pkEgWAK/QFrG5t5g6iT4PrcEQVENk8eSZ/TlHG8NB6lvP5xsfaFc+ZGEiqM2lHOnKOLZF6PuUx98NKeqKkerAtah5cAO72tfAqqST4d04YKDzLR7p8i4XVnatgmGcDmYyRUJhWzxq9FHyZ6li5Xgtkk2UrcmOKyYdIr0oaOaiI+IK1uIF3F3a0OGA9+2m4b1Hgdm5KpaQZmRGNcwinusVpyclbqcDvhj/jBJvaXIrgeJO7x9AAExQTQLbCxMg+RmYsFnP77858tkYMHTqEMZQsKxXheVvZ8jbMX345tRf4Zce154T6oCHHbxZMqsWNtChyv7Ko8AoiZWN20lJpS0u8PiCLUZmVOw1bI3Oddg9AxHGGbAT/Vcgud4JF8kxkz2WkWt3RlsvCpOOrwTwTSoHnVzLVnxUktMHLTMfl1tRI3M9gmEGH0ZH221D+S2QFDxga8tA1VJd+vCO1sgprAtfSqBWQkGwC+krPIkhGQn1A+DTJtSkoJNsi2yBP3254amYf2eVouZIs4sxG";
//        String sessionKey = "fzmUYWl9GYBT4+cIfNZ/Eg==";
//        String iv = "8rDOIx96pFTwGBfmYAcxaQ==";

//      微信小程序getUserInfo加密数据数据
        String encryptedData = "eE2Gh6efy8mJcJ2zs2sPtWlB2LHMpsCwiYy4M6ydMtraDeQJFWxkxnqgXSiijmzcp9tzOdSCdAyZFPzWnTq8tLDV4fS1S1A9rImmuKC8M6tikZFarCQqvStbHZXr6VFGS/RJmaKkplIT9cKwNOr9frtRJdtLctg3nwkU29ygwe7b+H+HAir+cdJL87J3YNTLLzjhoBDs3qP4Gw+LqstffzXr+bI7ZauqQbBq3LjkOWCraWU3qDtUzZHbaTDJzz6aeT5VM6gwxdeLqhTEcdUbDj9NQDP1bX108V6CbFSrF6h3e5Luhz+YTLw2z9kROb6MBX/O1K7YOLI7QFPwzYWCGwStK63tROgwyIZsv3O/SBedPxc5ihLmSuIXv/SrpayP6+cl7C2KXemux9wniRUgk7FPeBWWKDEGjrvGgJBX/nYTZCnvUs4VGikG6zdsF5fpDqxi3WQIUx3NfrU0VNdCo5IDYYiOrQSaRY27UzSnxrcV2PNeJnOeWzfe0PSCr6K3";
        String sessionKey = "stX+F8y7sqYZBuyai+ofEQ==";
        String iv = "xEFmdJGSxnnnv/rinJzCcg==";
        long start = System.currentTimeMillis();
        System.out.println(decrypt(sessionKey, encryptedData, iv));
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * 解密微信数据
     * <p>
     * 解密算法如下：
     * 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充。
     * 对称解密的目标密文为 Base64_Decode(encryptedData)。
     * 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
     * 对称解密算法初始向量 为Base64_Decode(iv)，其中iv由数据接口返回。
     *
     * @param sessionKey
     * @param encryptedData
     * @param iv
     * @return
     */
    public static JSONObject decrypt(String sessionKey, String encryptedData, String iv) {
        //目标密文
        byte[] encryptDataBytes = Base64.decode(encryptedData);
        //密钥
        byte[] sessionKeyBytes = Base64.decode(sessionKey);
        //解密算法初始向量
        byte[] ivBytes = Base64.decode(iv);

        int base = 16;
        if ((sessionKeyBytes.length % base) == 0) {
            int groups = sessionKeyBytes.length / base + (sessionKeyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(sessionKeyBytes, 0, temp, 0, sessionKeyBytes.length);
            sessionKeyBytes = temp;
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher;
            try {
                cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
                SecretKeySpec spec = new SecretKeySpec(sessionKeyBytes, "AES");
                AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
                parameters.init(new IvParameterSpec(ivBytes));
                // 初始化
                cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
                byte[] resultByte = cipher.doFinal(encryptDataBytes);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, "UTF-8");
                    return JSONUtil.parseObj(result);
                }
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchProviderException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (InvalidParameterSpecException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
